#pragma once

#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <string>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

using namespace std;

const char* pathname="./zll";
const int proj_id=0x54;


//4kb的倍数
const int default_size=4096;

string ToHex(key_t key)
{
    char buffer[1024];
    //输入16进制
    snprintf(buffer, sizeof(buffer), "0x%x", key);

    return buffer;
}


/**
 * 生成一个唯一的键值，用于消息队列、信号量或共享内存的标识
 * 
 * 此函数通过调用ftok函数，将一个路径名和一个项目标识符转换为一个键值
 * 如果ftok调用失败，会输出错误信息并终止程序
 * 
 * @return key_t 返回生成的键值
 */
key_t GetKey()
{
    // 将指定的路径名和项目标识符转换为键值
    key_t key=ftok(pathname, proj_id);
    
    // 检查ftok是否调用成功
    if(key==-1)
    {
        // ftok调用失败，输出错误信息并终止程序
        cerr<<"ftok error:"<<strerror(errno)<<endl;
        exit(1);
    }

    // 返回生成的键值
    return key;
}

//创建共享内存
int GreateShmOrDie(key_t key,int size,int flag)
{
    //key唯一键值，标识共享内存 size共享内存大小 flag共享内权限
    int shmid=shmget(key, size, flag);

    if(shmid==-1)
    {
        cerr<<"shmget error:"<<strerror(errno)<<endl;
        exit(1);
    }

    return shmid;
}


int CreateShm(key_t key,int size)
{

     // IPC_CREAT: 不存在就创建，存在就获取
    // IPC_EXCL: 没有意义
    // IPC_CREAT | IPC_EXCL: 不存在就创建，存在就出错返回

    return GreateShmOrDie(key, size, IPC_CREAT|IPC_EXCL|0666);
}

int GetShm(key_t key,int size)
{
    return GreateShmOrDie(key, size, IPC_CREAT);
}


void DeleteShm(int shmid)
{
    //shmid==key
    int ret=shmctl(shmid, IPC_RMID, nullptr);

    if(ret==-1)
    {
        cerr<<"shmctl error:"<<strerror(errno)<<endl;
        exit(1);
    }
    else
    {
        cout<<"delete shm success"<<endl;
    }
    
}

void ShmDebug(int shmid)
{
    struct shmid_ds shmds;

    int ret=shmctl(shmid, IPC_STAT, &shmds);

    if (ret==-1)
    {
        cerr<<"shmctl error:"<<strerror(errno)<<endl;
        return;
    }

    cout<<"shm size:"<<shmds.shm_segsz<<endl;
    cout<<"shm nattch:"<<shmds.shm_nattch<<endl;
    cout<<"shm ctime:"<<shmds.shm_ctime<<endl;
    cout << "shmds.shm_perm.__key:" << ToHex(shmds.shm_perm.__key) << endl;
    
}

/**
 * 连接到共享内存段
 * 
 * 此函数通过调用shmat函数将共享内存段附加到调用进程的地址空间
 * 它是进程间通信（IPC）的一部分，特别适用于需要共享数据的场景
 * 
 * @param shmid 共享内存标识符，由 shmget 函数返回
 * @return 返回附加到进程地址空间的共享内存段的指针
 *         如果附加失败，该函数会输出错误信息并终止程序
 */
void* ShmAttach(int shmid)
{
    // 将共享内存段附加到调用进程的地址空间
    void* ptr=shmat(shmid, nullptr, 0);

    // 检查共享内存段是否成功附加
    if(ptr==(void*)-1)
    {
        // 输出错误信息并终止程序
        cerr<<"shmat error:"<<strerror(errno)<<endl;
        exit(1);
    }

    // 返回共享内存段的指针
    return ptr;
}

/**
 * 从当前进程的地址空间中分离共享内存段。
 * 
 * @param ptr 指向要分离的共享内存段的指针。
 * 
 * 此函数调用shmdt来从当前进程分离共享内存段。如果分离失败，它会输出错误信息并退出程序。
 * 注意：此函数不检查ptr是否为nullptr，调用者需要确保ptr有效。
 */
void ShmDetach(void* ptr)
{
    // 尝试从进程地址空间中分离共享内存段
    int ret=shmdt(ptr);

    // 检查共享内存段是否成功分离
    if(ret==-1)
    {
        // 输出错误信息并退出程序，因为共享内存段分离失败
        cerr<<"shmdt error:"<<strerror(errno)<<endl;
        exit(1);
    }
}